#include "./BSP/DAC/bsp_dac.h"

DAC_HandleTypeDef g_dac_handle;         /* DAC句柄 */

/**
 * @brief       DAC初始化函数
 * @note        本函数支持DAC1_OUT1/2通道初始化
 *              - DAC的输入时钟来自APB1，频率为42MHz (周期23.8ns)
 *              - DAC在输出buffer关闭时，输出建立时间最小值为3us (STM32F407数据手册规定)
 *              - 理论最高输出速度：约333KHz，以10个点为一个周期时可输出约33KHz波形
 *
 * @param[in]   outx: 要初始化的通道
 *              @arg    1: 初始化DAC通道1 (PA4)
 *              @arg    2: 初始化DAC通道2 (PA5)
 * @retval      无
 */
void dac_init(uint8_t outx)
{
    __HAL_RCC_DAC_CLK_ENABLE();                                 /* 使能DAC1的时钟 */
    GPIO_InitTypeDef gpio_init_struct;
    __HAL_RCC_GPIOA_CLK_ENABLE();                               /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    gpio_init_struct.Pin = (outx==1) ? GPIO_PIN_4 : GPIO_PIN_5; /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG; 
    gpio_init_struct.Pull = GPIO_NOPULL;      
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);

    g_dac_handle.Instance = DAC;
    HAL_DAC_Init(&g_dac_handle);                                /* 初始化DAC */

    DAC_ChannelConfTypeDef DACCH1_Config;
    DACCH1_Config.DAC_Trigger = DAC_TRIGGER_NONE;               /* 不使用触发功能 */
    DACCH1_Config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;  /* DAC1输出缓冲关闭 */
    
    switch(outx)
    {
        case 1:
            HAL_DAC_ConfigChannel(&g_dac_handle, &DACCH1_Config, DAC_CHANNEL_1);    /* DAC通道1配置 */
            HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1);                            /* 开启DAC通道1 */
            break;
        case 2:
            HAL_DAC_ConfigChannel(&g_dac_handle, &DACCH1_Config, DAC_CHANNEL_2);    /* DAC通道2配置 */
            HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_2);                            /* 开启DAC通道2 */
            break;
        default : break;
    }
}

/* 声明外部变量 */
extern float g_vdda_voltage; 

/**
 * @brief       设置DAC通道输出电压
 * @param[in]   outx: 选择DAC通道
 *              @arg    1: DAC通道1 (PA4)
 *              @arg    2: DAC通道2 (PA5)
 * @param[in]   vol: 目标电压值，单位为mV
 *              范围: 0~3300，对应0~3.3V
 * @note        该函数使用实际测量的VDDA值作为参考，提高输出精度
 *              - 12位DAC分辨率，提供4096级电压输出
 *              - 输出电压 = (DAC值/4096) * VDDA
 * @retval      无
 */
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
    double temp = vol;
    temp /= 1000;

    /* 使用实际VDDA值代替固定的3.3V */
    temp = temp * 4096 / g_vdda_voltage;

    if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */

    if (outx == 1)  /* 通道1 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp);      /* 12位右对齐数据格式设置DAC值 */
    }
    else            /* 通道2 */
    {
        HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp);      /* 12位右对齐数据格式设置DAC值 */
    }
}

/**
 * @brief       设置DAC输出4~20mA电流
 * @param[in]   outx: 选择DAC通道
 *              @arg    1: DAC通道1 (PA4)
 *              @arg    2: DAC通道2 (PA5)
 * @param[in]   curr: 目标电流值，单位为μA
 *              范围: 0或4000~20000，对应0或4~20mA
 *              - 当curr=0时，关闭输出(设置为0mA)
 *              - 当0<curr<4000时，自动调整为4000(4mA)
 * @note        此函数用于工业控制应用，将DAC输出转换为标准4-20mA电流信号
 *              - 需要外部电压-电流转换电路
 *              - 转换关系: 1V对应10mA (100Ω采样电阻)
 *              - 4mA对应0.4V，20mA对应2.0V，0mA对应0V
 * @retval      无
 */
void dac_set_current_4_20ma(uint8_t outx, uint16_t curr)
{
    /* 特殊情况处理：curr=0表示关闭输出 */
    if (curr == 0)
    {
        /* 设置为0V输出，对应0mA电流 */
        dac_set_voltage(outx, 0);
        return;
    }
    
    /* 判断电流值是否在有效范围内 */
    if (curr < 4000) curr = 4000;  /* 标准4-20mA信号，最小值为4mA */
    if (curr > 20000) curr = 20000;
    
    /* 将电流值转换为电压值，关系为1V对应10mA */
    /* 计算方法: 电压(V) = 电流(mA) / 10 */
    double dac_volt = (double)curr / 10000.0;  /* 转换为V */
    
    /* 转换为dac_set_voltage函数所需的参数格式(mV) */
    uint16_t dac_value = (uint16_t)(dac_volt * 1000);
    
    /* 调用基础的设置电压函数 */
    dac_set_voltage(outx, dac_value);
}
